package com.ddwl.wallet.service;

import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.fastjson.JSON;
import com.ddwl.common.constant.*;
import com.ddwl.common.exception.ParamException;
import com.ddwl.common.feign.api.FundsApi;
import com.ddwl.common.feign.api.GateApi;
import com.ddwl.common.feign.api.MessageApi;
import com.ddwl.common.feign.api.UserApi;
import com.ddwl.common.util.BeanConverUtils;
import com.ddwl.common.util.MySqlUtil;
import com.ddwl.common.util.UUIDGenerator;
import com.ddwl.schema.bo.base.PageBo;
import com.ddwl.schema.bo.funds.WxEntPayBo;
import com.ddwl.schema.bo.gate.DingTalkBo;
import com.ddwl.schema.bo.gate.PushWxMsgBo;
import com.ddwl.schema.bo.gate.wechat.WxTemplateMsgBo;
import com.ddwl.schema.bo.message.TempSentSmsBo;
import com.ddwl.schema.bo.wallet.ShopSubmitWithdrawBo;
import com.ddwl.schema.bo.wallet.UserWithdrawSubmitBo;
import com.ddwl.schema.bo.wallet.WithdrawListQueryBo;
import com.ddwl.schema.util.DateUtil;
import com.ddwl.schema.vo.base.PageVo;
import com.ddwl.schema.vo.funds.WxEntPayVo;
import com.ddwl.schema.vo.wallet.UserWalletVo;
import com.ddwl.schema.vo.wallet.UserWithdrawRecordVo;
import com.ddwl.schema.vo.wallet.WithdrawChannelVo;
import com.ddwl.wallet.dao.WithdrawRecordDao;
import com.ddwl.wallet.dao.model.UserWithdrawRecord;
import com.ddwl.wallet.excel.ExportWithdraw;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.gitee.tooleek.lock.spring.boot.annotation.Lock;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedOutputStream;
import java.math.BigDecimal;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类Withdraw.java的实现描述：提现服务
 *
 * @author zach
 * @Date :2020-07-25 14:16.
 */
@Service
public class WithdrawService {

    @Resource
    private WithdrawRecordDao withdrawRecordDao;

    @Resource
    private UserWalletService userWalletService;

    @Resource
    private MessageApi messageApi;

    @Resource
    private FundsApi fundsApi;

    @Resource
    private WithdrawChannelService withdrawChannelService;
    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private UserApi userApi;
    @Resource
    private GateApi gateApi;

    /**
     * 提现费率默认千分之6
     *
     */
    private BigDecimal FEE_RATE = new BigDecimal("0.006");

    public PageVo<UserWithdrawRecordVo> queryWithdrawRecord(PageBo<WithdrawListQueryBo> bo){
        PageHelper.startPage(bo.getPage(),bo.getPageSize(), MySqlUtil.orderBy(bo.getSort(),bo.isAsc()));
        PageInfo<UserWithdrawRecord> info = new PageInfo(withdrawRecordDao.queryWithdrawRecord(bo.getParam()));
        return new PageVo<>(info.getTotal(),bo.getPage(),bo.getPageSize(),BeanConverUtils.convertBeanList(info.getList(),UserWithdrawRecordVo.class));
    }

    public void export(WithdrawListQueryBo bo, HttpServletResponse response) throws Exception{
        List<UserWithdrawRecord> userWithdrawRecords = withdrawRecordDao.queryWithdrawRecord(bo);
        List<ExportWithdraw> exportWithdrawVos = BeanConverUtils.convertBeanList(userWithdrawRecords, ExportWithdraw.class);
        String fileName = "提现记录_"+DateUtil.formatDate(Instant.now());
        response.reset();
        //设置响应域
        //中文名称要进行编码处理
        response.setHeader("Content-disposition", "attachment; filename="+new String(fileName.getBytes("GB2312"),"ISO8859-1")+".xls");
        response.setContentType("application/x-xls");
        //构建excelWriter
        ExcelWriter writer = EasyExcel
                //将数据映射到ExportWithdrawVo实体类并响应到浏览器
                .write(new BufferedOutputStream(response.getOutputStream()), ExportWithdraw.class)
                //自动关闭输出流
                .autoCloseStream(Boolean.TRUE)
                .excelType(ExcelTypeEnum.XLSX)
                //是否需要表头
                .needHead(Boolean.TRUE)
                //构建
                .build();
        //初始化sheet
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        writeSheet.setSheetNo(1);
        writeSheet.setSheetName("Sheet1");
        //导出excel，注意导出结束要finish
        writer.write(exportWithdrawVos,writeSheet);
        writer.finish();
    }

    public Integer queryWithdrawRecordList(WithdrawListQueryBo bo){
        Integer queryWithdrawRecordCount = withdrawRecordDao.queryWithdrawRecordCount(bo);
        return queryWithdrawRecordCount;
    }

    /**
     * 获取体现记录详情
     * @param flowId
     * @param userId
     * @return
     */
    public UserWithdrawRecordVo getUserWithdrawRecord(String flowId,String userId){
        UserWithdrawRecord record = withdrawRecordDao.selectByPrimaryKey(flowId);
        ParamException.isFalse(record.getId().equals(flowId),"越权访问数据");
        return BeanConverUtils.convertBean(record,UserWithdrawRecordVo.class);
    }

    /**
     * 用户提交提现
     * @param withdrawSubmitBo
     */
    @Lock(leaseTime = 10)
    @Transactional(rollbackFor = Exception.class)
    public synchronized UserWalletVo submitWithdraw(UserWithdrawSubmitBo withdrawSubmitBo){
        UserWalletVo vo = userWalletService.getUserWallet(withdrawSubmitBo.getUserId());
        ParamException.isTrue(vo.getAvailableAmount().compareTo(withdrawSubmitBo.getTotalAmount())<0,"账户余额不足");
        UserWithdrawRecord withdrawRecord = withdrawRecordDao.queryProcessingRecord(withdrawSubmitBo.getUserId());
        ParamException.isNotNull(withdrawRecord,"当前有正在处理中的提现申请,请稍后再提交");
        Integer count=withdrawRecordDao.countMonthByUserId(withdrawSubmitBo.getUserId());
        ParamException.isTrue(count > 2,"本月提现已到上限");
        UserWithdrawRecord record = BeanConverUtils.convertBean(withdrawSubmitBo,UserWithdrawRecord.class);
        record.setStatus(BaseEnum.ApplyEnum.CRE.getCode());
        record.setHandleTime(Instant.now());
        return userWallet(record);
    }


    /**
     * 店铺提交提现
     * @param withdrawSubmitBo
     */
    @Lock(leaseTime = 10)
    @Transactional(rollbackFor = Exception.class)
    public UserWalletVo shopSubmitWithdraw(ShopSubmitWithdrawBo withdrawSubmitBo){
        UserWalletVo vo = userWalletService.getUserWallet(withdrawSubmitBo.getUserId());
        ParamException.isTrue(vo.getAvailableAmount().compareTo(withdrawSubmitBo.getTotalAmount())<0,"账户余额不足");
        UserWithdrawRecord withdrawRecord = withdrawRecordDao.queryProcessingRecord(withdrawSubmitBo.getUserId());
        ParamException.isNotNull(withdrawRecord,"当前有正在处理中的提现申请,请稍后再提交");
        Integer count= withdrawRecordDao.countDayByUserId(withdrawSubmitBo.getUserId());
        ParamException.isTrue(count > 3,"当天的提现已到上限");
        WithdrawChannelVo withdrawChannelVo = withdrawChannelService.queryShopChannel(withdrawSubmitBo.getShopId());
        ParamException.isNull(withdrawChannelVo,"店铺银行卡信息不完整");
        UserWithdrawRecord record = BeanConverUtils.convertBean(withdrawSubmitBo,UserWithdrawRecord.class);
        record = assemblyUserWithdrawRecord(record,withdrawChannelVo);
        record.setStatus(BaseEnum.ApplyEnum.CRE.getCode());
        record.setHandleTime(Instant.now());
        return userWallet(record);
    }

    /**
     * 根据银行卡信息组装提现申请
     * @param record
     * @param withdrawChannelVo
     * @return
     */
    public UserWithdrawRecord assemblyUserWithdrawRecord(UserWithdrawRecord record,WithdrawChannelVo withdrawChannelVo){
        record.setAccount(withdrawChannelVo.getAccount());
        record.setAccountAddress(withdrawChannelVo.getBankAddress());
        record.setAccountName(withdrawChannelVo.getBankName());
        record.setChannelId(withdrawChannelVo.getId());
        record.setChannelType(withdrawChannelVo.getType());
        record.setMobile(withdrawChannelVo.getMobile());
        return record;
    }

    /**
     * 用户提现
     * @param record
     * @return
     */
    public UserWalletVo userWallet(UserWithdrawRecord record) {
        //暂时去除手续费，应改把手续费做成配置放在提现渠道配置中
        record.setFeeAmount(BigDecimal.ZERO);
        record.setRealAmount(record.getTotalAmount());
        record.setId(UUIDGenerator.getShortUUID());
        //冻结用户提现资金
        userWalletService.freezeUserAmount(record.getUserId(),record.getTotalAmount(),DistConstant.WalletFlowType.TX,record.getId());
        if (record.getRealAmount().compareTo(new BigDecimal("200")) <= 0) {
            if (PaymentEnum.WX.getCode().equals(record.getChannelType()) || PaymentEnum.BANK_CARD.getCode().equals(record.getChannelType())) {
                ((WithdrawService) AopContext.currentProxy()).wxEntPay(record);
            }else{
                sendDingMsg();
            }
        }else {
            record.setCheckFlag(BaseEnum.StatusEnum.ENABLE.getCode());
            sendDingMsg();
        }
        withdrawRecordDao.insert(record);
        return BeanConverUtils.convertBean(record,UserWalletVo.class);
    }

    private void sendDingMsg() {
        //发送钉钉消息
        DingTalkBo bo1 = new DingTalkBo();
        bo1.setMessage("");
        bo1.setChannel(StatisticsEnum.channel.UN_APPROVE_WITHDRAW_APPLY.getId());
        rabbitTemplate.convertAndSend(RabbitMqConstants.DINGDING_TALK_MESSAGE, JSON.toJSONString(bo1));
    }

    @Async
    public void wxEntPay(UserWithdrawRecord record) {
        WxEntPayBo bo = new WxEntPayBo();
        bo.setPayType(record.getChannelType());
        bo.setMchAppid(BaseEnum.WxAppId.WBM_APP.getCode());
        if (PaymentEnum.WX.getCode().equals(record.getChannelType())) {
            bo.setOpenid(record.getAccount());
            bo.setReUserName(record.getUserName());
        }else if (PaymentEnum.BANK_CARD.getCode().equals(record.getChannelType())) {
            if (StrUtil.isEmpty(record.getWxCode())) {
                sendDingMsg();
                return;
            }
            bo.setEncBankNo(record.getAccount());
            bo.setEncTrueName(record.getUserName());
            bo.setBankCode(record.getWxCode());
        }
        bo.setPartnerTradeNo(record.getId());
        bo.setAmount(record.getRealAmount().multiply(new BigDecimal("100")).intValue());
        bo.setNotifyQueue(RabbitMqConstants.WX_ENT_PAY_QUEUE);
        fundsApi.entPay(bo);
    }

    @Async
    public  void nofity(UserWithdrawRecord record){
        ParamException.isTrue(true);
        //微信通知发送接口
        PushWxMsgBo pushWxMsgBo=new PushWxMsgBo();
        pushWxMsgBo.setUserId(record.getUserId());
        Map<String,String> templateDate=new HashMap<>();
        pushWxMsgBo.setTemplateNum(WxEnum.WxMsgTemplate.TEMPLATEID2.getCode());
        templateDate.put(WxEnum.TEMPLATEINVOKE2.TOTALAMOUNT.getCode(),record.getTotalAmount().toString());
        templateDate.put(WxEnum.TEMPLATEINVOKE2.FEEAMOUNT.getCode(),(record.getFeeAmount()==null?new BigDecimal("0.0"):record.getFeeAmount()).toString());
        templateDate.put(WxEnum.TEMPLATEINVOKE2.WITHDRAWTIME.getCode(), DateUtil.formatDate(record.getWithdrawTime(),DateUtil.DATE_TIME_FORMAT3));
        pushWxMsgBo.setTemplateDate(templateDate);
    }

    public void dealWithNotify(WxEntPayVo wxEntPayVo) {
        if (WalletConstant.WithdrawResult.FAILED.name().equals(wxEntPayVo.getStatus())) {
            paymentFail(wxEntPayVo.getPartnerTradeNo(), wxEntPayVo.getReason());
        }
        if(WalletConstant.WithdrawResult.SUCCESS.name().equals(wxEntPayVo.getStatus())){
            paymentSuccess(wxEntPayVo.getPartnerTradeNo(),wxEntPayVo.getPaymentNo());
        }
    }

    /**
     * 打款失败
     */
    @Transactional(rollbackFor = Exception.class)
    public void  paymentFail(String id,String msg){
        UserWithdrawRecord  record =  withdrawRecordDao.selectByPrimaryKey(id);
        ParamException.isNull(record);
        ParamException.isFalse(BaseEnum.ApplyEnum.CRE.getCode().equals(record.getStatus()),"提现流水状态不正确");
        ParamException.isNull(record);
        UserWithdrawRecord record1 = new UserWithdrawRecord();
        record1.setId(id);
        record1.setStatus(BaseEnum.ApplyEnum.FILL.getCode());
        record1.setErrorMsg(msg);
        withdrawRecordDao.updateByPrimaryKeySelective(record1);
        userWalletService.unfreezeUserAmount(record.getUserId(),record.getTotalAmount(), DistConstant.WalletFlowType.TX_FALL,id);
        record.setStatus(BaseEnum.ApplyEnum.FILL.getCode());
        record.setErrorMsg(msg);
        ((WithdrawService) AopContext.currentProxy()).asyncMessage(record);
    }

    @Transactional(rollbackFor = Exception.class)
    public void  paymentSuccess(String id, String payFlowNo){
        UserWithdrawRecord  record =  withdrawRecordDao.selectByPrimaryKey(id);
        ParamException.isNull(record);
        ParamException.isFalse(BaseEnum.ApplyEnum.CRE.getCode().equals(record.getStatus()),"提现流水状态不正确");
        UserWithdrawRecord record1 = new UserWithdrawRecord();
        record1.setId(id);
        record1.setHandleTime(Instant.now());
        record1.setStatus(BaseEnum.ApplyEnum.SUCCEED.getCode());
        record1.setPayFlow(payFlowNo);
        withdrawRecordDao.updateByPrimaryKeySelective(record1);
        userWalletService.deductFreezeUserAmount(record.getUserId(),record.getTotalAmount());
        record.setHandleTime(record1.getHandleTime());
        record.setStatus(BaseEnum.ApplyEnum.SUCCEED.getCode());
        record.setPayFlow(payFlowNo);
        ((WithdrawService) AopContext.currentProxy()).asyncMessage(record);
    }

    /**
     * 转账
     * @param id
     * @param payFlowNo
     */
    @Transactional(rollbackFor = Exception.class)
    public void  payment(String id, String payFlowNo){
        UserWithdrawRecord  record =  withdrawRecordDao.selectByPrimaryKey(id);
        ParamException.isNull(record);
        ParamException.isFalse(BaseEnum.ApplyEnum.CRE.getCode().equals(record.getStatus()),"提现流水状态不正确");
        if (BaseEnum.StatusEnum.ENABLE.getCode().equals(record.getCheckFlag()) && PaymentEnum.WX.getCode().equals(record.getChannelType())) {
            ((WithdrawService) AopContext.currentProxy()).wxEntPay(record);
        } else {
            paymentSuccess(id, payFlowNo);
        }
    }

    @Async
    public void asyncMessage(UserWithdrawRecord  record){
        //sendSMS(record);
        pushWechatMsg(record);
    }

    /**
     * 发送短信
     */
    private void sendSMS(UserWithdrawRecord  record){
        TempSentSmsBo bo = new TempSentSmsBo();
        bo.setToPhone(record.getMobile());
        if (BaseEnum.ApplyEnum.SUCCEED.getCode().equals(record.getStatus())) {
            bo.setTemplateCode(SmsTempCodeEnum.USER_WITHDRAW.getCode());
            List<String> list = new ArrayList<>();
            list.add(record.getTotalAmount().toString());
            list.add(record.getFeeAmount().toString());
            list.add(record.getRealAmount().toString());
            bo.setTemplateParam(list);
        }else if(BaseEnum.ApplyEnum.FILL.getCode().equals(record.getStatus())){
            String tel = gateApi.getSettingVal(GateEnum.DictionaryTag.TAG_SYSTEM.name(), GateEnum.DictionaryField.CONSUMER_HOTLINE.name()).getResult();
            bo.setTemplateCode(SmsTempCodeEnum.USER_WITHDRAW_FAIL.getCode());
            List<String> list = new ArrayList<>();
            list.add(record.getTotalAmount().toString());
            list.add(record.getErrorMsg());
            list.add(tel);
            bo.setTemplateParam(list);
        }
        messageApi.sentSms(bo);
    }

    private void pushWechatMsg(UserWithdrawRecord record) {
        if (null != record) {
            String openId = userApi.getWechatOpenId(record.getUserId()).getResult();
            if (StrUtil.isNotEmpty(openId)) {
                WxTemplateMsgBo template = new WxTemplateMsgBo();
                Instant time = null == record.getHandleTime() ? record.getGmtCreated() : record.getHandleTime();
                String timeStr = DateUtil.formatDateTime(time);
                record.getChannelType();
                String channelName = WalletEnum.WithdrawChannelEnum.getChannelName(record.getChannelType());
                template.setTouser(openId);
                template.setTopcolor("#353535");
                WxTemplateMsgBo.TemplateData data = template.new TemplateData();
                if (BaseEnum.ApplyEnum.SUCCEED.getCode().equals(record.getStatus())) {
                    template.setTemplate_id(WechatTemplateEnum.WITHDRAW_SUCCESS.getTemplateId());
                    data.setFirst(template.new Node("尊敬的"+record.getUserName()+"，您申请的提现金额已到帐：","#54529E"));
                    data.setKeyword1(template.new Node(timeStr, "#54529E"));
                    data.setKeyword2(template.new Node(channelName, "#54529E"));
                    data.setKeyword3(template.new Node(record.getTotalAmount()+"元", "#54529E"));
                    data.setKeyword4(template.new Node(record.getFeeAmount()+"元", "#54529E"));
                    data.setKeyword5(template.new Node(record.getRealAmount()+"元", "#54529E"));
                }else if(BaseEnum.ApplyEnum.FILL.getCode().equals(record.getStatus())){
                    template.setTemplate_id(WechatTemplateEnum.WITHDRAW_FAIL.getTemplateId());
                    data.setFirst(template.new Node("尊敬的"+record.getUserName()+"，您的提现申请失败，资金已退回账户：","#54529E"));
                    data.setKeyword1(template.new Node(record.getTotalAmount()+"元", "#54529E"));
                    data.setKeyword2(template.new Node(timeStr, "#54529E"));
                    data.setKeyword3(template.new Node(BaseEnum.ApplyEnum.FILL.getName(), "#54529E"));
                    data.setKeyword4(template.new Node(record.getErrorMsg(), "#54529E"));
                }
                data.setRemark(template.new Node("感谢你的使用！","#353535"));
                template.setData(data);
                gateApi.tempMessagePush(template);
            }
        }
    }



}
